Video

# lm1 <- lm(formula = tenure ~ age + year_num + AB + OPS, data = people_tenure)
# 
# # people_tenure_preds <- people_tenure %>%
# #   mutate(tenure_pred = lm1$fitted.values,
# #          resids = lm1$residuals)
# 
# tidy(lm1)
# 
# yardstick::metrics(data = people_tenure_preds, truth = tenure, estimate = tenure_pred)

# library(GGally)
# 
# people_tenure %>%
#   # select(-c(playerID, yearID, nameFirst, nameLast, birthDate, 
#   #           debut, finalGame, stint, teamID, lgID)) %>%
#   select(tenure, age, year_num, TAB, OPS) %>%
#   ggpairs()
# 
# people_tenure %>%
#   # select(-c(playerID, yearID, nameFirst, nameLast, birthDate, 
#   #           debut, finalGame, stint, teamID, lgID)) %>%
#   select(tenure, G, R, H, X2B, HR, TBB) %>%
#   ggpairs()
# 
# people_tenure %>%
#   # select(-c(playerID, yearID, nameFirst, nameLast, birthDate, 
#   #           debut, finalGame, stint, teamID, lgID)) %>%
#   select(tenure, SB, SO, TB, SlugPct, OBP) %>%
#   ggpairs()
# data.frame(age = c(33),
#            year_num = c(10),
#            TAB = c(600),
#            OPS = c(1)) %>%
#   predict(lm_theory, newdata = .)
# performance::check_outliers(lm_theory)
# Lahman::Fielding %>% head()
# Lahman::Salaries %>% head()
# Lahman::Appearances %>% arrange(desc(yearID)) %>% head()
# Lahman::AllstarFull %>% head()

# fielding <- Lahman::Fielding %>%
#   group_by(playerID, yearID) %>%
#   arrange(desc(G)) %>%
#   mutate(pos_rank = row_number()) %>%
#   ungroup() %>%
#   filter(pos_rank == 1) %>%
#   select(playerID, yearID, POS, G, GS, InnOuts, PO, A, E) %>%
#   anti_join(old_players) %>%
#   rename(G_pos = G)
# 
# appearances <- Lahman::Appearances %>%
#   group_by(playerID, yearID) %>%
#   summarise(G_batting = sum(G_batting),
#             G_defense = sum(G_defense),
#             G_dh = sum(G_dh),
#             G_ph = sum(G_ph))
# 
# people_tenure_all <- people_tenure %>%
#   left_join(fielding, by = c("playerID", "yearID")) %>%
#   replace_na(list(G_pos = 0, GS = 0, InnOuts = 0, PO = 0, A = 0, E = 0)) %>%
#   mutate(fielding_pct = (PO + A) / (PO + A + E)) %>%
#   group_by(POS) %>%
#   mutate(field_pct_scaled = scale(fielding_pct)) %>%
#   ungroup() %>%
#   left_join(appearances, by = c("playerID", "yearID"))



# people_tenure <- people_tenure %>%
#   left_join(appearances, by = c("playerID", "yearID")) %>%
  

To do

General modeling outline

  1. build simple baseline model
  2. build simple blackbox model
  3. analyze residuals from blackbox model
  4. feature engineer to try and correct large residuals
# tidymodels
# library(rsample)
# library(recipes)
# library(parsnip)
# library(tune)
# library(dials)
# library(workflows)
# library(yardstick)
# run this if treesnip not installed: remotes::install_github("curso-r/treesnip")

Most important hyperparameters in xgBoost: https://blog.dataiku.com/narrowing-the-search-which-hyperparameters-really-matter#:~:text=We%20again%20found%20the%20most,be%20seen%20in%20figure%203.

Top 5:

  1. learning rate
  2. subsample
  3. min_child_weight
  4. colsample_bytree
  5. max_depth
# xgb_grid <- grid_latin_hypercube(
#   learn_rate(),
#   sample_size = sample_prop(),
#   min_n(),
#   tree_depth(),
#   trees(),
#   finalize(mtry(), pt_train),
#   loss_reduction(),
#  
#   size = 100
# )
# 
# xgb_grid
# xgb_wf <- workflow() %>%
#   add_formula(tenure ~ year_num + age + TAB + OPS) %>%
#   add_model(xgb_spec)
# 
# xgb_wf
# pt_folds <- vfold_cv(pt_train, v = 5)
# 
# pt_folds
# library(xgboost)
# library(doParallel)
# doParallel::registerDoParallel()
# 
# set.seed(234)
# xgb_res <- tune_grid(
#   xgb_wf,
#   resamples = pt_folds,
#   grid = xgb_grid #,
#   #control = control_grid(save_pred = TRUE)
# )
# 
# xgb_res
# collect_metrics(xgb_res)
# show_best(xgb_res, "rsq")
# best_rsq <- select_best(xgb_res, "rsq")
# best_rsq
# final_xgb <- finalize_workflow(
#   xgb_wf,
#   best_rsq
# )
# 
# final_xgb
# # install.packages("vip")
# library(vip)
# 
# final_xgb %>%
#   fit(data = pt_train) %>%
#   pull_workflow_fit() %>%
#   vip(geom = "col")
# 
# final_res <- last_fit(final_xgb, pt_split)
# 
# collect_metrics(final_res)
# 
# set.seed(123)
# # https://www.tidymodels.org/learn/work/bayes-opt/
# # https://towardsdatascience.com/which-evaluation-metric-should-you-use-in-machine-learning-regression-problems-20cdaef258e
# 
# xgb_set <- parameters(xgb_wf)
# 
# search_res <-
#   xgb_wf %>% 
#   tune_bayes(
#     resamples = pt_folds,
#     # To use non-default parameter ranges
#     param_info = xgb_set,
#     # Generate five at semi-random to start
#     initial = 10,
#     iter = 50,
#     # How to measure performance?
#     metrics = metric_set(rmse),
#     control = control_bayes(no_improve = 30, verbose = TRUE)
#   )
# 
# show_best(search_res, metric = "rmse")
# best_rsq_bayes <- select_best(search_res, "rmse")
# xgb <- boost_tree(
#   # learn_rate = 0.02,          ## step size
#   # sample_size = .5,
#   # min_n = 38,
#   # # mtry = tune(),
#   # tree_depth = 6, 
#   # #colsample_bytree = tune(),
#   # trees = 1000, 
#   # loss_reduction = 0.001,      ## first three: model complexity
#   # #              ## randomness
#   
#     
# ) %>% 
#   set_engine("xgboost") %>% 
#   set_mode("regression")
# 
# xgb_workflow <- workflow() %>%
#   add_formula(tenure ~ year_num + age + TAB + OPS) %>%
#   add_model(xgb)
# 
# xgb_fit <- 
#   xgb_workflow %>%
#   fit(data = pt_train)

#https://rviews.rstudio.com/2019/06/19/a-gentle-intro-to-tidymodels/

# theory_formula <- formula(tenure ~ year_num + age + TAB + OPS)
# 
# xgb_theory <- boost_tree(mode = "regression") %>%
#   set_engine("xgboost") %>%
#   fit(theory_formula, data = pt_train)
# 
# xgb_theory %>%
#   predict(pt_test) %>%
#   bind_cols(pt_test) %>%
#   metrics(truth = tenure, estimate = .pred)
# xgb_spec <- boost_tree(
#   learn_rate = tune(),          ## step size
#   sample_size = tune(),
#   min_n = tune(),
#   # mtry = tune(),
#   tree_depth = tune(), 
#   #colsample_bytree = tune(),
#   trees = tune(), 
#   loss_reduction = tune(),      ## first three: model complexity
#   #              ## randomness
#   
#     
# ) %>% 
#   set_engine("xgboost") %>% 
#   set_mode("regression")
# 
# xgb_spec
# xgb_wf2 <- workflow() %>%
#   add_formula(tenure ~ year_num + age + TAB + OPS + TB + H + G + TBB + X2B + HR + SO + age + X3B + BABIP) %>%
#   add_model(xgb_spec)
# 
# xgb_res2 <- tune_grid(
#   xgb_wf2,
#   resamples = pt_folds,
#   grid = xgb_grid,
#   control = control_grid(save_pred = TRUE)
# )
# 
# show_best(xgb_res2, "rsq")
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFZpZGVvDQoNCjx2aWRlbyB3aWR0aD0iMzIwIiBoZWlnaHQ9IjI0MCIgY29udHJvbHM+DQogIDxzb3VyY2Ugc3JjPSJtb3N0LXhUQi1wdWpvbHMubXA0IiB0eXBlPSJ2aWRlby9tcDQiPg0KPC92aWRlbz4NCg0KYGBge3J9DQojIGxtMSA8LSBsbShmb3JtdWxhID0gdGVudXJlIH4gYWdlICsgeWVhcl9udW0gKyBBQiArIE9QUywgZGF0YSA9IHBlb3BsZV90ZW51cmUpDQojIA0KIyAjIHBlb3BsZV90ZW51cmVfcHJlZHMgPC0gcGVvcGxlX3RlbnVyZSAlPiUNCiMgIyAgIG11dGF0ZSh0ZW51cmVfcHJlZCA9IGxtMSRmaXR0ZWQudmFsdWVzLA0KIyAjICAgICAgICAgIHJlc2lkcyA9IGxtMSRyZXNpZHVhbHMpDQojIA0KIyB0aWR5KGxtMSkNCiMgDQojIHlhcmRzdGljazo6bWV0cmljcyhkYXRhID0gcGVvcGxlX3RlbnVyZV9wcmVkcywgdHJ1dGggPSB0ZW51cmUsIGVzdGltYXRlID0gdGVudXJlX3ByZWQpDQpgYGANCg0KDQoNCmBgYHtyfQ0KDQojIGxpYnJhcnkoR0dhbGx5KQ0KIyANCiMgcGVvcGxlX3RlbnVyZSAlPiUNCiMgICAjIHNlbGVjdCgtYyhwbGF5ZXJJRCwgeWVhcklELCBuYW1lRmlyc3QsIG5hbWVMYXN0LCBiaXJ0aERhdGUsIA0KIyAgICMgICAgICAgICAgIGRlYnV0LCBmaW5hbEdhbWUsIHN0aW50LCB0ZWFtSUQsIGxnSUQpKSAlPiUNCiMgICBzZWxlY3QodGVudXJlLCBhZ2UsIHllYXJfbnVtLCBUQUIsIE9QUykgJT4lDQojICAgZ2dwYWlycygpDQojIA0KIyBwZW9wbGVfdGVudXJlICU+JQ0KIyAgICMgc2VsZWN0KC1jKHBsYXllcklELCB5ZWFySUQsIG5hbWVGaXJzdCwgbmFtZUxhc3QsIGJpcnRoRGF0ZSwgDQojICAgIyAgICAgICAgICAgZGVidXQsIGZpbmFsR2FtZSwgc3RpbnQsIHRlYW1JRCwgbGdJRCkpICU+JQ0KIyAgIHNlbGVjdCh0ZW51cmUsIEcsIFIsIEgsIFgyQiwgSFIsIFRCQikgJT4lDQojICAgZ2dwYWlycygpDQojIA0KIyBwZW9wbGVfdGVudXJlICU+JQ0KIyAgICMgc2VsZWN0KC1jKHBsYXllcklELCB5ZWFySUQsIG5hbWVGaXJzdCwgbmFtZUxhc3QsIGJpcnRoRGF0ZSwgDQojICAgIyAgICAgICAgICAgZGVidXQsIGZpbmFsR2FtZSwgc3RpbnQsIHRlYW1JRCwgbGdJRCkpICU+JQ0KIyAgIHNlbGVjdCh0ZW51cmUsIFNCLCBTTywgVEIsIFNsdWdQY3QsIE9CUCkgJT4lDQojICAgZ2dwYWlycygpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQojIGRhdGEuZnJhbWUoYWdlID0gYygzMyksDQojICAgICAgICAgICAgeWVhcl9udW0gPSBjKDEwKSwNCiMgICAgICAgICAgICBUQUIgPSBjKDYwMCksDQojICAgICAgICAgICAgT1BTID0gYygxKSkgJT4lDQojICAgcHJlZGljdChsbV90aGVvcnksIG5ld2RhdGEgPSAuKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgcGVyZm9ybWFuY2U6OmNoZWNrX291dGxpZXJzKGxtX3RoZW9yeSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBMYWhtYW46OkZpZWxkaW5nICU+JSBoZWFkKCkNCiMgTGFobWFuOjpTYWxhcmllcyAlPiUgaGVhZCgpDQojIExhaG1hbjo6QXBwZWFyYW5jZXMgJT4lIGFycmFuZ2UoZGVzYyh5ZWFySUQpKSAlPiUgaGVhZCgpDQojIExhaG1hbjo6QWxsc3RhckZ1bGwgJT4lIGhlYWQoKQ0KDQpgYGANCg0KYGBge3J9DQoNCiMgZmllbGRpbmcgPC0gTGFobWFuOjpGaWVsZGluZyAlPiUNCiMgICBncm91cF9ieShwbGF5ZXJJRCwgeWVhcklEKSAlPiUNCiMgICBhcnJhbmdlKGRlc2MoRykpICU+JQ0KIyAgIG11dGF0ZShwb3NfcmFuayA9IHJvd19udW1iZXIoKSkgJT4lDQojICAgdW5ncm91cCgpICU+JQ0KIyAgIGZpbHRlcihwb3NfcmFuayA9PSAxKSAlPiUNCiMgICBzZWxlY3QocGxheWVySUQsIHllYXJJRCwgUE9TLCBHLCBHUywgSW5uT3V0cywgUE8sIEEsIEUpICU+JQ0KIyAgIGFudGlfam9pbihvbGRfcGxheWVycykgJT4lDQojICAgcmVuYW1lKEdfcG9zID0gRykNCiMgDQojIGFwcGVhcmFuY2VzIDwtIExhaG1hbjo6QXBwZWFyYW5jZXMgJT4lDQojICAgZ3JvdXBfYnkocGxheWVySUQsIHllYXJJRCkgJT4lDQojICAgc3VtbWFyaXNlKEdfYmF0dGluZyA9IHN1bShHX2JhdHRpbmcpLA0KIyAgICAgICAgICAgICBHX2RlZmVuc2UgPSBzdW0oR19kZWZlbnNlKSwNCiMgICAgICAgICAgICAgR19kaCA9IHN1bShHX2RoKSwNCiMgICAgICAgICAgICAgR19waCA9IHN1bShHX3BoKSkNCiMgDQojIHBlb3BsZV90ZW51cmVfYWxsIDwtIHBlb3BsZV90ZW51cmUgJT4lDQojICAgbGVmdF9qb2luKGZpZWxkaW5nLCBieSA9IGMoInBsYXllcklEIiwgInllYXJJRCIpKSAlPiUNCiMgICByZXBsYWNlX25hKGxpc3QoR19wb3MgPSAwLCBHUyA9IDAsIElubk91dHMgPSAwLCBQTyA9IDAsIEEgPSAwLCBFID0gMCkpICU+JQ0KIyAgIG11dGF0ZShmaWVsZGluZ19wY3QgPSAoUE8gKyBBKSAvIChQTyArIEEgKyBFKSkgJT4lDQojICAgZ3JvdXBfYnkoUE9TKSAlPiUNCiMgICBtdXRhdGUoZmllbGRfcGN0X3NjYWxlZCA9IHNjYWxlKGZpZWxkaW5nX3BjdCkpICU+JQ0KIyAgIHVuZ3JvdXAoKSAlPiUNCiMgICBsZWZ0X2pvaW4oYXBwZWFyYW5jZXMsIGJ5ID0gYygicGxheWVySUQiLCAieWVhcklEIikpDQoNCg0KDQojIHBlb3BsZV90ZW51cmUgPC0gcGVvcGxlX3RlbnVyZSAlPiUNCiMgICBsZWZ0X2pvaW4oYXBwZWFyYW5jZXMsIGJ5ID0gYygicGxheWVySUQiLCAieWVhcklEIikpICU+JQ0KICANCmBgYA0KDQoNCiMgVG8gZG8NCg0KLSBhbmFseXNlIGJpZyByZXNpZHVhbHMNCi0gZmVhdHVyZSBlbmdpbmVlciB0byB0cnkgYW5kIGZpeCBiaWcgcmVzaWR1YWxzDQotIHRyYWluIHVzaW5nIHhnQm9vc3QNCg0KIyBHZW5lcmFsIG1vZGVsaW5nIG91dGxpbmUNCg0KMS4gYnVpbGQgc2ltcGxlIGJhc2VsaW5lIG1vZGVsDQoyLiBidWlsZCBzaW1wbGUgYmxhY2tib3ggbW9kZWwNCjMuIGFuYWx5emUgcmVzaWR1YWxzIGZyb20gYmxhY2tib3ggbW9kZWwNCjQuIGZlYXR1cmUgZW5naW5lZXIgdG8gdHJ5IGFuZCBjb3JyZWN0IGxhcmdlIHJlc2lkdWFscw0KDQoNCg0KDQpgYGB7cn0NCiMgdGlkeW1vZGVscw0KIyBsaWJyYXJ5KHJzYW1wbGUpDQojIGxpYnJhcnkocmVjaXBlcykNCiMgbGlicmFyeShwYXJzbmlwKQ0KIyBsaWJyYXJ5KHR1bmUpDQojIGxpYnJhcnkoZGlhbHMpDQojIGxpYnJhcnkod29ya2Zsb3dzKQ0KIyBsaWJyYXJ5KHlhcmRzdGljaykNCiMgcnVuIHRoaXMgaWYgdHJlZXNuaXAgbm90IGluc3RhbGxlZDogcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImN1cnNvLXIvdHJlZXNuaXAiKQ0KDQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCk1vc3QgaW1wb3J0YW50IGh5cGVycGFyYW1ldGVycyBpbiB4Z0Jvb3N0OiBodHRwczovL2Jsb2cuZGF0YWlrdS5jb20vbmFycm93aW5nLXRoZS1zZWFyY2gtd2hpY2gtaHlwZXJwYXJhbWV0ZXJzLXJlYWxseS1tYXR0ZXIjOn46dGV4dD1XZSUyMGFnYWluJTIwZm91bmQlMjB0aGUlMjBtb3N0LGJlJTIwc2VlbiUyMGluJTIwZmlndXJlJTIwMy4NCg0KVG9wIDU6ICANCg0KMS4gbGVhcm5pbmcgcmF0ZSANCjIuIHN1YnNhbXBsZQ0KMy4gbWluX2NoaWxkX3dlaWdodA0KNC4gY29sc2FtcGxlX2J5dHJlZQ0KNS4gbWF4X2RlcHRoDQpgYGB7cn0NCiMgeGdiX2dyaWQgPC0gZ3JpZF9sYXRpbl9oeXBlcmN1YmUoDQojICAgbGVhcm5fcmF0ZSgpLA0KIyAgIHNhbXBsZV9zaXplID0gc2FtcGxlX3Byb3AoKSwNCiMgICBtaW5fbigpLA0KIyAgIHRyZWVfZGVwdGgoKSwNCiMgICB0cmVlcygpLA0KIyAgIGZpbmFsaXplKG10cnkoKSwgcHRfdHJhaW4pLA0KIyAgIGxvc3NfcmVkdWN0aW9uKCksDQojICANCiMgICBzaXplID0gMTAwDQojICkNCiMgDQojIHhnYl9ncmlkDQpgYGANCg0KYGBge3J9DQojIHhnYl93ZiA8LSB3b3JrZmxvdygpICU+JQ0KIyAgIGFkZF9mb3JtdWxhKHRlbnVyZSB+IHllYXJfbnVtICsgYWdlICsgVEFCICsgT1BTKSAlPiUNCiMgICBhZGRfbW9kZWwoeGdiX3NwZWMpDQojIA0KIyB4Z2Jfd2YNCmBgYA0KDQpgYGB7cn0NCiMgcHRfZm9sZHMgPC0gdmZvbGRfY3YocHRfdHJhaW4sIHYgPSA1KQ0KIyANCiMgcHRfZm9sZHMNCmBgYA0KDQoNCmBgYHtyfQ0KIyBsaWJyYXJ5KHhnYm9vc3QpDQojIGxpYnJhcnkoZG9QYXJhbGxlbCkNCiMgZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKCkNCiMgDQojIHNldC5zZWVkKDIzNCkNCiMgeGdiX3JlcyA8LSB0dW5lX2dyaWQoDQojICAgeGdiX3dmLA0KIyAgIHJlc2FtcGxlcyA9IHB0X2ZvbGRzLA0KIyAgIGdyaWQgPSB4Z2JfZ3JpZCAjLA0KIyAgICNjb250cm9sID0gY29udHJvbF9ncmlkKHNhdmVfcHJlZCA9IFRSVUUpDQojICkNCiMgDQojIHhnYl9yZXMNCmBgYA0KDQpgYGB7cn0NCiMgY29sbGVjdF9tZXRyaWNzKHhnYl9yZXMpDQpgYGANCg0KDQpgYGB7cn0NCiMgc2hvd19iZXN0KHhnYl9yZXMsICJyc3EiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgYmVzdF9yc3EgPC0gc2VsZWN0X2Jlc3QoeGdiX3JlcywgInJzcSIpDQojIGJlc3RfcnNxDQpgYGANCg0KYGBge3J9DQojIGZpbmFsX3hnYiA8LSBmaW5hbGl6ZV93b3JrZmxvdygNCiMgICB4Z2Jfd2YsDQojICAgYmVzdF9yc3ENCiMgKQ0KIyANCiMgZmluYWxfeGdiDQpgYGANCg0KYGBge3J9DQojICMgaW5zdGFsbC5wYWNrYWdlcygidmlwIikNCiMgbGlicmFyeSh2aXApDQojIA0KIyBmaW5hbF94Z2IgJT4lDQojICAgZml0KGRhdGEgPSBwdF90cmFpbikgJT4lDQojICAgcHVsbF93b3JrZmxvd19maXQoKSAlPiUNCiMgICB2aXAoZ2VvbSA9ICJjb2wiKQ0KIyANCiMgZmluYWxfcmVzIDwtIGxhc3RfZml0KGZpbmFsX3hnYiwgcHRfc3BsaXQpDQojIA0KIyBjb2xsZWN0X21ldHJpY3MoZmluYWxfcmVzKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCmBgYHtyfQ0KIyANCiMgc2V0LnNlZWQoMTIzKQ0KIyAjIGh0dHBzOi8vd3d3LnRpZHltb2RlbHMub3JnL2xlYXJuL3dvcmsvYmF5ZXMtb3B0Lw0KIyAjIGh0dHBzOi8vdG93YXJkc2RhdGFzY2llbmNlLmNvbS93aGljaC1ldmFsdWF0aW9uLW1ldHJpYy1zaG91bGQteW91LXVzZS1pbi1tYWNoaW5lLWxlYXJuaW5nLXJlZ3Jlc3Npb24tcHJvYmxlbXMtMjBjZGFlZjI1OGUNCiMgDQojIHhnYl9zZXQgPC0gcGFyYW1ldGVycyh4Z2Jfd2YpDQojIA0KIyBzZWFyY2hfcmVzIDwtDQojICAgeGdiX3dmICU+JSANCiMgICB0dW5lX2JheWVzKA0KIyAgICAgcmVzYW1wbGVzID0gcHRfZm9sZHMsDQojICAgICAjIFRvIHVzZSBub24tZGVmYXVsdCBwYXJhbWV0ZXIgcmFuZ2VzDQojICAgICBwYXJhbV9pbmZvID0geGdiX3NldCwNCiMgICAgICMgR2VuZXJhdGUgZml2ZSBhdCBzZW1pLXJhbmRvbSB0byBzdGFydA0KIyAgICAgaW5pdGlhbCA9IDEwLA0KIyAgICAgaXRlciA9IDUwLA0KIyAgICAgIyBIb3cgdG8gbWVhc3VyZSBwZXJmb3JtYW5jZT8NCiMgICAgIG1ldHJpY3MgPSBtZXRyaWNfc2V0KHJtc2UpLA0KIyAgICAgY29udHJvbCA9IGNvbnRyb2xfYmF5ZXMobm9faW1wcm92ZSA9IDMwLCB2ZXJib3NlID0gVFJVRSkNCiMgICApDQojIA0KIyBzaG93X2Jlc3Qoc2VhcmNoX3JlcywgbWV0cmljID0gInJtc2UiKQ0KIyBiZXN0X3JzcV9iYXllcyA8LSBzZWxlY3RfYmVzdChzZWFyY2hfcmVzLCAicm1zZSIpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIHhnYiA8LSBib29zdF90cmVlKA0KIyAgICMgbGVhcm5fcmF0ZSA9IDAuMDIsICAgICAgICAgICMjIHN0ZXAgc2l6ZQ0KIyAgICMgc2FtcGxlX3NpemUgPSAuNSwNCiMgICAjIG1pbl9uID0gMzgsDQojICAgIyAjIG10cnkgPSB0dW5lKCksDQojICAgIyB0cmVlX2RlcHRoID0gNiwgDQojICAgIyAjY29sc2FtcGxlX2J5dHJlZSA9IHR1bmUoKSwNCiMgICAjIHRyZWVzID0gMTAwMCwgDQojICAgIyBsb3NzX3JlZHVjdGlvbiA9IDAuMDAxLCAgICAgICMjIGZpcnN0IHRocmVlOiBtb2RlbCBjb21wbGV4aXR5DQojICAgIyAjICAgICAgICAgICAgICAjIyByYW5kb21uZXNzDQojICAgDQojICAgICANCiMgKSAlPiUgDQojICAgc2V0X2VuZ2luZSgieGdib29zdCIpICU+JSANCiMgICBzZXRfbW9kZSgicmVncmVzc2lvbiIpDQojIA0KIyB4Z2Jfd29ya2Zsb3cgPC0gd29ya2Zsb3coKSAlPiUNCiMgICBhZGRfZm9ybXVsYSh0ZW51cmUgfiB5ZWFyX251bSArIGFnZSArIFRBQiArIE9QUykgJT4lDQojICAgYWRkX21vZGVsKHhnYikNCiMgDQojIHhnYl9maXQgPC0gDQojICAgeGdiX3dvcmtmbG93ICU+JQ0KIyAgIGZpdChkYXRhID0gcHRfdHJhaW4pDQoNCiNodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE5LzA2LzE5L2EtZ2VudGxlLWludHJvLXRvLXRpZHltb2RlbHMvDQoNCiMgdGhlb3J5X2Zvcm11bGEgPC0gZm9ybXVsYSh0ZW51cmUgfiB5ZWFyX251bSArIGFnZSArIFRBQiArIE9QUykNCiMgDQojIHhnYl90aGVvcnkgPC0gYm9vc3RfdHJlZShtb2RlID0gInJlZ3Jlc3Npb24iKSAlPiUNCiMgICBzZXRfZW5naW5lKCJ4Z2Jvb3N0IikgJT4lDQojICAgZml0KHRoZW9yeV9mb3JtdWxhLCBkYXRhID0gcHRfdHJhaW4pDQojIA0KIyB4Z2JfdGhlb3J5ICU+JQ0KIyAgIHByZWRpY3QocHRfdGVzdCkgJT4lDQojICAgYmluZF9jb2xzKHB0X3Rlc3QpICU+JQ0KIyAgIG1ldHJpY3ModHJ1dGggPSB0ZW51cmUsIGVzdGltYXRlID0gLnByZWQpDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIHhnYl9zcGVjIDwtIGJvb3N0X3RyZWUoDQojICAgbGVhcm5fcmF0ZSA9IHR1bmUoKSwgICAgICAgICAgIyMgc3RlcCBzaXplDQojICAgc2FtcGxlX3NpemUgPSB0dW5lKCksDQojICAgbWluX24gPSB0dW5lKCksDQojICAgIyBtdHJ5ID0gdHVuZSgpLA0KIyAgIHRyZWVfZGVwdGggPSB0dW5lKCksIA0KIyAgICNjb2xzYW1wbGVfYnl0cmVlID0gdHVuZSgpLA0KIyAgIHRyZWVzID0gdHVuZSgpLCANCiMgICBsb3NzX3JlZHVjdGlvbiA9IHR1bmUoKSwgICAgICAjIyBmaXJzdCB0aHJlZTogbW9kZWwgY29tcGxleGl0eQ0KIyAgICMgICAgICAgICAgICAgICMjIHJhbmRvbW5lc3MNCiMgICANCiMgICAgIA0KIyApICU+JSANCiMgICBzZXRfZW5naW5lKCJ4Z2Jvb3N0IikgJT4lIA0KIyAgIHNldF9tb2RlKCJyZWdyZXNzaW9uIikNCiMgDQojIHhnYl9zcGVjDQpgYGANCg0KDQoNCg0KYGBge3J9DQojIHhnYl93ZjIgPC0gd29ya2Zsb3coKSAlPiUNCiMgICBhZGRfZm9ybXVsYSh0ZW51cmUgfiB5ZWFyX251bSArIGFnZSArIFRBQiArIE9QUyArIFRCICsgSCArIEcgKyBUQkIgKyBYMkIgKyBIUiArIFNPICsgYWdlICsgWDNCICsgQkFCSVApICU+JQ0KIyAgIGFkZF9tb2RlbCh4Z2Jfc3BlYykNCiMgDQojIHhnYl9yZXMyIDwtIHR1bmVfZ3JpZCgNCiMgICB4Z2Jfd2YyLA0KIyAgIHJlc2FtcGxlcyA9IHB0X2ZvbGRzLA0KIyAgIGdyaWQgPSB4Z2JfZ3JpZCwNCiMgICBjb250cm9sID0gY29udHJvbF9ncmlkKHNhdmVfcHJlZCA9IFRSVUUpDQojICkNCiMgDQojIHNob3dfYmVzdCh4Z2JfcmVzMiwgInJzcSIpDQpgYGANCg0KDQpgYGB7cn0NCg0KYGBgDQoNCg==